package org.NooLab.compare.utilities.math;

import org.apache.commons.math.linear.RealMatrix;
import org.apache.commons.math.linear.RealMatrixImpl;
import org.apache.commons.math.stat.descriptive.moment.VectorialCovariance;

/**
 * imported from: http://code.google.com/p/datamine/ data mining library
 * featuring supervised learning implemented with decision trees.
 * 
 * Mahalanobis distance = sqrt(m_weights * cov * vec)

 * User: alex Date: 6-Jun-2009 Time: 5:22:57 PM
 * 
 * See http://en.wikipedia.org/wiki/Mahalanobis_distance
 */
public class Mahalanobis {
	// --------------------------------------------------------------------
	// --------------------------------------------------------------------
	private final double sums[];
	private double count;
	private double means[];

	private final VectorialCovariance covariance;
	private RealMatrix covarianceMatrix;

	// --------------------------------------------------------------------
	public Mahalanobis(int dimensions) {
		sums = new double[dimensions];
		means = null;

		covariance = new VectorialCovariance(dimensions, false);
		covarianceMatrix = null;
	}

	// --------------------------------------------------------------------
	public void add(double values[], double valueCount) {
		for (int i = 0; i < valueCount; i++) {
			add(values);
		}
	}

	public void add(double values[]) {
		covarianceMatrix = null;

		try {
			covariance.increment(values);
		} catch (Exception e) {
			throw new Error(e);
		}

		for (int i = 0; i < values.length; i++) {
			sums[i] += values[i];
		}
		count++;
	}

	// --------------------------------------------------------------------
	@SuppressWarnings("deprecation")
	public double distance(double to[]) {
		assert count > 0;

		if (covarianceMatrix == null) {
			covarianceMatrix = covariance.getResult();
		}

		if (means == null) {
			means = new double[sums.length];
			for (int i = 0; i < means.length; i++) {
				means[i] = sums[i] / count;
			}
		}

		RealMatrix uT = new RealMatrixImpl(means);
		RealMatrix xT = new RealMatrixImpl(to);

		RealMatrix xMinusU = xT.subtract(uT);
		RealMatrix sInverse = covarianceMatrix.inverse();

		RealMatrix distSquared = xMinusU.transpose().multiply(sInverse) .multiply(xMinusU);

		return Math.sqrt(distSquared.getEntry(0, 0));
	}
	
	public void test(){
		Mahalanobis dist = new Mahalanobis(2);
		// VectorialCovariance covariance =
		// new VectorialCovariance(2, false);

		for (int i = 0; i < 10; i++) {
			dist.add(new double[] { 5 + Math.random() * 5,
					6 + Math.random() * 10 });
			// covariance.increment(new double[]{
			// Rand.nextDouble(),
			// Rand.nextDouble()});
		}

		// System.out.println(
		// covariance.getResult());
		// System.out.println(
		// covariance.getResult().inverse());
		System.out.println(dist.distance(new double[] { 6.0, 8.0 }));
	}


}
